#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>

//the qt stuff
#include <Qt>
#include <QtCore>
#include <QFile>
#include <QDir>

#ifdef unix
    #include <sys/types.h>
    #include <dirent.h>
#else
    // for file things
    #include <direct.h>
#endif

#include <errno.h>


#include "db.h" 
using namespace std;

/**
 * Return back a list of files (should be fixed to work in windows also)
 * @param dir_name : the name of directory to scan
 * @param file_list : the thing to turn back
 * retun : nothing actually it does
 */
int DbOperation::listImages(string dir_name,vector<string> &file_list){
    string  ALLOWED_EXTENTIONS[] ={"jpeg","JPG","jpg","png","bmp","gif","pgm"};

    DIR *dp;
    struct dirent *dirp;
    if((dp  = opendir(dir_name.c_str())) == NULL) {
        cout << "Error(" << errno << ") opening " << dir_name << endl;
        return errno;
        }
    
    while ((dirp = readdir(dp)) != NULL) {

        // Need some control for extentions
        bool found = false ;
        
        //spltting the stuff from extention
        QString file_ext(string(dirp->d_name).c_str());

        //split it from the extention part
       // cout << "The current file is :" << file_ext.toStdString() <<endl;
        if(file_ext.startsWith((QString)"."))
            continue;

        QStringList file_params=file_ext.split(".");

        if(file_params.size()!=2)
            continue;

        //cout << "The length of the parsed is :" << file_params.size() << endl;
        for(int i=0;i<7;i++){
            //cout << "The current extention is :" << file_params.at(1).toStdString()<<"  "<<ALLOWED_EXTENTIONS[i].c_str()<<endl;
            if(strcmp(ALLOWED_EXTENTIONS[i].c_str(),file_params.at(1).toStdString().c_str())==0){
                found = true;
                break;

            }
        }

        if(found == false)
            continue;

        file_list.push_back(string(dirp->d_name));
                    }
    closedir(dp);

    if (file_list.size()==0){
        cout << "no matching pattern file" << endl; 
        return (-2);
    }


    return 0;

}

/**
 * Just prints them
 */
int  DbOperation::printImages(string dir_name){

    //define the man to turn back
    vector<string> flist = vector <string>();
    
   if( (this->listImages(dir_name,flist))!=0){
        cout << "Some error in listing "<<endl;
        return -1;
   }
        
    cout << "The file list is :" << endl;

    for(unsigned int i=0; i<flist.size();i++){
        
        ImageOpt *parsed_name = new ImageOpt();
        this->parseImageName(flist[i],parsed_name);
        //collect the garbage 
        delete parsed_name;
        //cout << flist[i] << endl;
    }
    return 0;
}


bool DbOperation::parseImageName(string filename,ImageOpt *parsed_name){

    QString temp_str(filename.c_str());

    //first check if the format is valid actually we may move that part
    //into another function if we wish ??
    //assuming id_name_part.pgm
    QStringList ql = temp_str.split(".");

    if(ql.size()!=2){
        cout << "The file with wrong extention ?" <<endl;
        return false;
    }

    QStringList ql_under = ql.at(0).split("_");

    if(ql_under.size()!=3){
        cout << "The underscores are id_name_part" << endl;
        return false;
    }

    //daha da guzel yapilabilir ama simdilik bole olsun fixleriz...
    parsed_name->id = atoi(ql_under.at(0).toStdString().c_str());
    strcpy(parsed_name->person_name,ql_under.at(1).toStdString().c_str());
    parsed_name->part = atoi(ql_under.at(2).toStdString().c_str());

    //cout << "*******************************************" << endl;
    //cout << "The parsed file is :" << endl;
    //cout << "id : " << (parsed_name->id) << endl;
    //cout << "name : " << (parsed_name->person_name) << endl;
    //cout << "id : " << (parsed_name->part) << endl;

    //all the stuff should be done here 
    return true;
}

/**
 * Moving a file in a platform independent way with qt
 * @param : they are obvious
 * @param type : if 0 copy if 1 move
 * return : if succesful
 */

bool DbOperation::moveFile(char *source_dir,char *dest_dir,char *source_file,char *dest_file,int type){

      //make them qtish
      QString sf_str(source_file);
      QString sd_str(source_dir);
      QString df_str(dest_file);
      QString dd_str(dest_dir);


      //Make the absolute path for destination 
      dd_str.append("/");
      dd_str.append(df_str);

      //The path for destination is :
      //cout << "The destination path is :"<<dd_str.toStdString() << endl; 
      sd_str.append("/");
      sd_str.append(sf_str);
     

      //The files
      QFile df_file;
      df_file.setFileName(dd_str);
      QFile sf_file;
      sf_file.setFileName(sd_str);

     //cout << "The source file is :"<<sd_str.toStdString() << endl;
    
    if(df_file.exists()){
         cout << "The file in the dest already exists :" << df_str.toStdString()<<endl;
         if(!df_file.remove())
            return false;
      }
      
     if (!sf_file.copy(dd_str)){
          cout << "Copying unsuccesfull" << endl;
          return false;
      }

     // if it is a move
     if(type==1)
      sf_file.remove();


      return true;
}

/**
 * That one adds an image to db test one with specified name
 * @param image_name : the file's name (or maybe the absolute path)
 * @param dir_name : the test dirs place
 * @param person_name : the persons name
 *
 * return : true if succesfull
 */


bool DbOperation::addImage(char *image_name,char *dir_name,char *person_name,char* src_dir){

        //firstly take the image list and generate a filename for current
        
        //dont forget to delete it
        ImageOpt *to_add_info=new ImageOpt();
        int num_images;

        //now get the all list from dir
        
         vector<string> flist = vector <string>();
         if( (this->listImages(dir_name,flist))!=0){
             cout << "Some error in listing "<<endl;
            return false;                        
         }

         num_images = flist.size();

         int max_id = 1; //the max id to find it
         //create a QMap object

        QList<ImageOpt*> dblist;
        
        ImageOpt **temp_ops_struct= new ImageOpt*[num_images];

         for(int i=0;i<num_images;i++){
            temp_ops_struct[i] = new ImageOpt();

            if (this->parseImageName(flist[i],temp_ops_struct[i]))
            {
                if ((temp_ops_struct[i]->id)>max_id){
                    max_id = temp_ops_struct[i]->id;

                }

                //make here some stuff for list
                if (strcmp(temp_ops_struct[i]->person_name,person_name)!=0){
                    continue;
                }

                else{
                    dblist.append(temp_ops_struct[i]);
                    
                }
            }


         }

        // firstly the easy part if it is first name in that category:
        if(dblist.count()==0){
            //cout << "The name does not exist so it is a new entry here " << endl;
            to_add_info->id = (max_id+1);
            strcpy(to_add_info->person_name,person_name);
            to_add_info->part = 1;
        }

        // the harder part
        else{
            //cout << "The lis size is " << dblist.count() << endl;
            //cout << "The name is in db we should chose a part for it " << endl;
            int the_max_part = 1; // which image is it ?

            for (int i = 0; i < dblist.count(); i++) {
                //cout << "The real part is :"<<dblist.at(i)->part << "and the maxpart is :"<< the_max_part << endl;

                if ((dblist.at(i)->part) > (the_max_part)){
                    the_max_part = dblist.at(i)->part;
                }
            }

            //Store the result to new filename 
            to_add_info->id = dblist.at(0)->id; // just take one man
            to_add_info->part = (the_max_part+1);
            strcpy((to_add_info->person_name),person_name);
            //cout << "The info is : id :"<< to_add_info->id << "person_name :" << to_add_info->person_name << "part :" << to_add_info->part << endl;

        }

        char real_filename[80];
        sprintf(real_filename,"%d_%s_%d.pgm",to_add_info->id,to_add_info->person_name,to_add_info->part);
        cout << "The file to be copied is :"<<real_filename<<endl;

        if (this->moveFile(src_dir,dir_name,image_name,real_filename,0)==false){ // just copy it 
            //cout << "The copying failed man" << endl;    
            delete to_add_info;
            to_add_info = NULL;

            return false;

            }
        //here just give it free
        delete to_add_info;
        to_add_info = NULL;

        //free the ones you take before
        delete [] temp_ops_struct;
        temp_ops_struct= NULL;

        return true;
}


/*
 * Adding all directory to database, it is needed when someone
 * wants to add the stuff at once
 * @param dir_name : where to store them
 * @param from_dir : from where
 * @param person_name : whose are they 
 *
 * return : true if success
 * */

 bool DbOperation::addAllDir(char* dir_name,char* person_name,char* from_dir){
 
    //firstly get all the images that should be added to database
    vector<string> flist = vector <string>();
        if( (this->listImages(from_dir,flist))!=0){
            cout << "Some error in listing "<<endl;
            return false;
        }

        if(flist.size()==0)
            return false;

       for(int unsigned i=0;i<(flist.size());i++){
    
        char temp[80];
        strcpy(temp,flist[i].c_str());

        if(!(this->addImage (temp,dir_name,person_name,from_dir)))
           cout << "The image couldnt be processed :" << flist[i] << endl;

    }

    //all the stuff completed here
    return true;


 }

bool DbOperation::removeImage(char *image_name, char *dir_name){
      
    
      //some qtish stuff for easiness and relaxing work ;)
      QString df_str(image_name);
      QString dd_str(dir_name);

      //The path for destination is :
      //cout << "The destination path is :"<<dd_str.toStdString() << endl; 
      dd_str.append("/");
      dd_str.append(df_str);
     

      //The files
      QFile df_file;
      df_file.setFileName(dd_str);

     //cout << "The source file is :"<<sd_str.toStdString() << endl;
    
    if(df_file.exists()){
         cout << "The file to remove is  :" << df_str.toStdString()<<endl;
         if(!df_file.remove()){
             cout << "Error in deleting the file sorry" << endl;
             return false;

         }
     }

    else{
        cout << "The file doesnt exist couldnt delete" << endl;
        return false;
    }
     
    //sucess
    return true;
}

/**
 * Removing all imaes that related to that person
 * @param person_name : the one to remove
 * @param dir_name : the place of images 
 *
 * return true if succesfull
 *
 */

bool DbOperation::removePerson(char* person_name,char* dir_name){
   
    vector<string> flist = vector <string>();
    if( (this->listImages(dir_name,flist))!=0){
        cout << "Some error in listing "<<endl;
        return false;
    }

    int num_images = flist.size();

    if(num_images==0){
        cout << "Nothing to delete here :"<< endl;
        return false;
    }

    
    vector<int> *del_vector=new vector<int>();

    for(int i=0;i<num_images;i++){
    
        ImageOpt *img = new ImageOpt();
        if(this->parseImageName(flist[i],img)){
            
            //if we have it should be deleted
            if(strcmp(img->person_name,person_name)==0){
                del_vector->push_back(i); 

            }
        
        }
        else{
            delete img;
            img=NULL;
            continue;
        }

        delete img;
        img=NULL;
        

    }

    if(del_vector->size()==0){
        cout << "No matching person to delete" << endl;
        return false;
    }

    for(unsigned int i=0;i<(del_vector->size());i++){

        char temp[80];
        strcpy(temp,flist[del_vector->at(i)].c_str());
        if(!(this->removeImage(temp,dir_name))){
            cout << "Error in deleting image"<<endl;
        }
    }

    //success
    delete del_vector;

    return true;

}

//*************************************************************************************************************

